# -*- coding: utf-8 -*-
'''
Created on 07/03/2011

@author: cgaray
@contact: cdaniel.py@gmail.com
@summary: representa los datos de estructura de una tabla de la base de datos
'''


#importamos los modulos necesarios
from CdgPyPersistencia.componentes.Campo import Campo
from decimal import Decimal

class TBLBase(object):
    '''
    representa los datos de una tabla de la base de datos
    '''
    
    #atributos de la clase
    NOMBRE_CLASE        = """TBLBase"""
    
    NOMBRE_TABLA        = ""
    CAMPOS              = {}
    
    SELECT_BASICO       = """SELECT %(campos)s FROM %(tabla)s """
    
    _CREATE_SQL          = ""
    _DROP_SQL            = "DROP TABLE %(tabla)s"
    
    _FILTRO_WHERE_INT   = """ WHERE %(tabla)s.%(campo)s = %(#)d """
    _FILTRO_WHERE_STR   = """ WHERE %(tabla)s.%(campo)s = '%(valor)s\%' """
    
    __INSERT_SQL        = """INSERT INTO %(tabla)s (%(campos)s) VALUES (%(valores)s) """
    __UPDATE_SQL        = """UPDATE %(tabla)s SET %(pares)s WHERE %(where)s """
    __DELETE_SQL        = """DELETE FROM %(tabla)s WHERE %(where)s """
    
    TIPO_NUMERICO       = [int, long, float, Decimal]
    TIPO_CARACTER       = [str, bool]
    
    
    def __init__(self, cNombreTabla):
        '''inicializador de la clase
        cNombreTabla = Nombre de la tabla a representar
        '''
        
        #tomamos el nombre de la tabla a representar
        self.NOMBRE_TABLA = cNombreTabla
        
    
    
    def Lista_de_campos(self):
        '''
        devuelve la lista de campos separados por coma
        CAMPO0, CAMPO1, CAMPO2,...
        '''
        
        #variable para resultado del metodo
        __cCampos = ""
        
        __l = self.CAMPOS.items()
        
        __cCampos= __cCampos.join(cad[1].cNombre + ", " for cad in sorted(__l, key = lambda __l: __l[0]))
            
        
        #si NO se formo la cadena, valor por defecto
        if __cCampos is "": __cCampos = "*"
        else: #eliminamos los ultimos dos caracteres (", ")
            __cCampos = __cCampos[:-2]
        
        #devolvemos el resultado del metodo
        return __cCampos
        
    
    
    def Lista_de_campos_tabla(self):
        '''
        devuelve la lista de campos separados por coma incluyendo el nombre de la tabla
        TABLA.CAMPO0, TABLA.CAMPO1, TABLA.CAMPO2,...
        '''
        
        
        #variable para resultado del metodo
        __cCampos = ""
        
        __l = self.CAMPOS.items()
        
        __cCampos= __cCampos.join(self.NOMBRE_TABLA + "." + cad[1].cNombre + ", " for cad in sorted(__l, key = lambda __l: __l[0]))
        
        #si NO se formo la cadena, valor por defecto
        if __cCampos is "": __cCampos = self.NOMBRE_TABLA + ".*"
        else: #eliminamos los ultimos dos caracteres (", ")
            __cCampos = __cCampos[:-2]
        
        #devolvemos el resultado del metodo
        return __cCampos
        
    
    
    def Lista_de_campos_dml(self):
        '''
        devuelve la lista de campos separados por coma para operaciones DML
        CAMPO0, CAMPO1, CAMPO2,...
        '''
        
        #variable para resultado del metodo
        __cCampos = ""
        
        #tomamos los items del diccionario de campos
        __l = self.CAMPOS.items()
        
        #recorremos la lista de nombres de campos
        for tCampo in sorted(__l, key = lambda __l: __l[1]):
            #si el campo no es autogenerado, lo agregamos a la cadena
            if tCampo[1].bAutoGenerado is False: __cCampos += tCampo[1].cNombre + ", "
            
        
        #devolvemos el resultado del metodo
        return __cCampos[:-2]
        
    
    
    def Lista_de_campos_parametros(self):
        '''
        devuelve un diccionario con los nombres de campos y sus nombres de parametros
        [CAMPO0 : @CAMPO0], [CAMPO0 : @CAMPO1]...
        '''
        
        #variable para resultado del metodo
        __dicParametros = {}
        
        #tomamos los items del diccionario de campos
        __l = self.CAMPOS.items()
        
        #recorremos la lista de campos
        for cCampo in sorted(__l, key = lambda __l: __l[0]):
            #lo agregamos al diccionario
            __dicParametros[cCampo[1]] = "@" + cCampo[1]
            
        
        #devolvemos el resultado del metodo
        return __dicParametros
        
    
    
    def Select_basico(self):
        '''
        devuelve la sentencia basica de consulta de registros
        SELECT CAMPOS FROM TABLA
        '''
        
        return self.SELECT_BASICO % {"campos":self.Lista_de_campos_tabla()
                                    , "tabla":self.NOMBRE_TABLA}
        
    
    
    def Get_insert_basico(self):
        ''''
        devuelve la sentencia basica de insercion de un registro de la tabla
        INSERT INTO TABLA (CAMPO0, CAMPO1) VALUES (%(valores)s)
        '''
        
        #ensamblamos la sentencia
        __cInsert = self.__INSERT_SQL % {"tabla":self.NOMBRE_TABLA
                                         , "campos":self.Lista_de_campos_dml()
                                         , "valores":"%(valores)s"
                                         }
        
        #devolvemos el resultado del metodo
        return __cInsert
        
    
    
    def Get_insert_parametros(self):
        ''''
        devuelve la sentencia de insercion de un registro de la tabla
        con nombres de parametros
        INSERT INTO TABLA (CAMPO0, CAMPO1) VALUES (%(campo0)s, %(campo1)s)
        '''
        
        #variables auxiliares
        __cInsert   = ""
        __cCampos   = ""
        __oCampo    = Campo
        __l         = self.CAMPOS.items()
        
        #recorremos la lista de campos
        for __tCampo in sorted(__l, key = lambda __l: __l[1]):
            #tomamos la instancia del campo
            __oCampo = __tCampo[1]
            
            #si el tipo de dato del campo es string
            if (__oCampo.tTipo in self.TIPO_CARACTER 
                and __oCampo.bAutoGenerado is False): 
                __cCampos += "'%(" + __oCampo.cNombre.lower() + ")s', " 
                
            elif (__oCampo.tTipo in self.TIPO_NUMERICO
                and __oCampo.bAutoGenerado is False):
                #sino
                __cCampos += "%(" + __oCampo.cNombre.lower() + ")s, "
                
        #eliminamos los ultimos dos caracteres (", ")
        __cCampos = __cCampos[:-2]
        
        #ensamblamos la sentencia
        __cInsert = self.__INSERT_SQL % {"tabla":self.NOMBRE_TABLA
                                         , "campos":self.Lista_de_campos_dml()
                                         , "valores":__cCampos
                                         }
        
        #devolvemos el resultado del metodo
        return __cInsert
        
    
    
    def Get_update_basico(self):
        ''''
        devuelve la sentencia basica de actualizacion de un registro de la tabla
        UPDATE TABLA SET %(pares)s WHERE %(where)s
        '''
        
        #ensamblamos la sentencia
        __cUpdate = self.__UPDATE_SQL % {"tabla":self.NOMBRE_TABLA
                                         , "pares":"%(pares)s"
                                         , "where":"%(where)s"
                                         }
        
        #devolvemos el resultado del metodo
        return __cUpdate
        
    
    
    def Get_update_parametros(self):
        ''''
        devuelve la sentencia basica de actualizacion de un registro de la tabla
        con nombres de parametros
        UPDATE TABLA SET CAMPO0 = %(campo0)s, CAMPO1 = %(campo1)s) WHERE %(where)s
        '''
        
        #variables auxiliares
        __cUpdate   = ""
        __cPares   = ""
        __oCampo    = Campo
        __l         = self.CAMPOS.items()
        
        #recorremos la lista de campos
        for __tCampo in sorted(__l, key = lambda __l: __l[1]):
            #tomamos la instancia del campo
            __oCampo = __tCampo[1]
            
            #si el tipo de dato del campo es string
            if (__oCampo.tTipo in self.TIPO_CARACTER 
                and __oCampo.bAutoGenerado is False): 
                __cPares += __oCampo.cNombre + " = '%(" + __oCampo.cNombre.lower() + ")s', " 
                
            elif __oCampo.bAutoGenerado is False:
                #sino
                __cPares += __oCampo.cNombre + " = %(" + __oCampo.cNombre.lower() + ")s, "
                
            
        #eliminamos los ultimos dos caracteres (", ")
        __cPares = __cPares[:-2]
        
        #ensamblamos la sentencia
        __cUpdate = self.__UPDATE_SQL % {"tabla":self.NOMBRE_TABLA
                                         , "pares":__cPares
                                         , "where":"%(where)s"
                                         }
        
        #devolvemos el resultado del metodo
        return __cUpdate
        
    
    
    def Get_delete_basico(self):
        ''''
        devuelve la sentencia basica de eliminacion de un registro de la tabla
        DELETE FROM TABLA WHERE %(where)s
        '''
        
        #ensamblamos la sentencia
        __cDelete = self.__DELETE_SQL % {"tabla":self.NOMBRE_TABLA
                                         , "where":"%(where)s"
                                         }
        
        #devolvemos el resultado del metodo
        return __cDelete
        
    
    
    
    def Get_nombre_tabla(self):
        '''
        devuelve el nombre de la tabla
        '''
        
        return TBLBase.NOMBRE_TABLA
        
    
    
    def __str__(self):
        '''representacion de la clase'''
        
        return "<%s: %s [%s]>" % (self.NOMBRE_CLASE, self.NOMBRE_TABLA, str(self.Lista_de_campos()))
        
    
    
    def  Set_campos(self, lCamposParam):
        '''
        ejecuta la asignacion y orden de los campos para su uso posterior
        '''
        
        #objetos auxiliares
        __oCampo = Campo
        
        #reseteamos el diccionario anterior
        self.CAMPOS = {}
        
        #recorremos el listado de campos parametros
        for __oCampo in lCamposParam:
            #lo agregamos al diccionario de campos
            self.CAMPOS[__oCampo.nIndice] = __oCampo
            
            
        
    
    
    def Get_create_sql(self):
        '''
        devuelve el comando de creacion de la tabla
        '''
        
        return "Metodo Get_create_sql() No implementado!"
        
        
    
    
    def Get_drop_sql(self):
        '''
        devuelve el comando de eliminacion de la tabla
        '''
        
        return self._DROP_SQL % {"tabla":self.Get_nombre_tabla()}
        
        
    
    
    
    
#lanzador de prueba del modulo
if __name__ == "__main__":
    
    #implementacion de herencia basica
    class MiTablaTBL (TBLBase):
        
        #definicion de campos de la tabla 
        ID          = Campo(nIndice= 0, cNombre= "ID", tTipo = type(0L), bAutogenerado = True)
        DESCRIPCION = Campo(nIndice= 1, cNombre= "DESCRIPCION", tTipo = type(""))
        
        
        #esto seria sobreescribir el metodo de la clase base
        def __init__(self):
            '''ínicializador de la clase hija'''
            
            #asignacion del nombre de la tabla
            self.NOMBRE_TABLA = "TEST_TBL"
            
            #asignacion de diccionarion de campos (normal)
            #self.CAMPOS = {self.ID.nIndice:self.ID, self.DESCRIPCION.nIndice:self.DESCRIPCION }
            
            #asignacion de diccionarion de campos (por metodo nuevo)
            self.Set_campos([self.ID, self.DESCRIPCION])
            
        
        
        #un metodo de prueba
        def test(self):
            print "NOMBRE TEST de Tabla", self.NOMBRE_TABLA
            
    
    
    #instancia de la clase hija
    t = MiTablaTBL()
    
    #prueba de metodos
    print t
    
    print t.Select_basico()
    
    print t.Get_insert_basico() % {"valores":"111, 'hola mundo cruel'"}
    print t.Get_update_basico() % {"pares":"descripcion = 'hola mundo cruelisimoooo'", "where": "ID = 1"}
    print t.Get_delete_basico() % {"where": "ID = 1"}
    
    print t.Get_insert_parametros() % {"id":1L, "descripcion":"hola mundo cruel"}
    print t.Get_update_parametros() % {"id":1L, "descripcion":"hola mundo cruelisimoooo", "where": "ID = 1"}
    
    